package com.softaria.spkiller.analyzer.hierarchy.enclosed;

import java.util.ArrayList;
import java.util.Collection;


import com.softaria.spkiller.analyzer.hierarchy.Hierarchy;
import com.softaria.spkiller.analyzer.hierarchy.HierarchyNode;
import com.softaria.spkiller.layers.LayerIdentifier;
import com.softaria.spkiller.layers.classificator.Classificator;
import com.softaria.spkiller.metadata.ClassIdentifier;

public class EnclosedHierarchyChecker {

	class SpeciesDeterminer {
		private Classificator classificator;
		private LayerIdentifier sheepsLayer;
		private LayerIdentifier shepherdLayer;

		public SpeciesDeterminer(Classificator classificator,
				LayerIdentifier sheepsLayer, LayerIdentifier shepherdLayer) {
			super();
			this.classificator = classificator;
			this.sheepsLayer = sheepsLayer;
			this.shepherdLayer = shepherdLayer;
		}

		public boolean isSheep(HierarchyNode<?> node) {
			LayerIdentifier layer = classificator.classificate(node
					.getClassIdentifier());

			return sheepsLayer.equals(layer);
		}

		public boolean isShepherd(HierarchyNode<?> node) {
			LayerIdentifier layer = classificator.classificate(node
					.getClassIdentifier());

			return shepherdLayer.equals(layer);
		}

	}

	private Collection<Hierarchy<Void>> shepherds;
	private SpeciesDeterminer determinator;
	private Collection<Hierarchy<SheepMarker>> sheeps;
	
	public EnclosedHierarchyChecker(Collection<Hierarchy<Void>> shepherds,
			Collection<Hierarchy<SheepMarker>> sheeps,
			Classificator classificator, LayerIdentifier shepherdLayer,
			LayerIdentifier sheepsLayer) {
		this.shepherds = shepherds;
		this.sheeps = sheeps;
		this.determinator =  new SpeciesDeterminer(classificator,
				sheepsLayer, shepherdLayer);
	}

	public void checkEnclosed(EnclosedHierarchyProblemReporter problemReporter) {

		for (Hierarchy<?> shepherdHierarchy : shepherds) {
			traverseshepherdTree(shepherdHierarchy.getRoot(),
					 problemReporter);
		}

	}

	private void traverseshepherdTree(HierarchyNode<?> node,
			EnclosedHierarchyProblemReporter problemReporter) {
		Collection<? extends HierarchyNode<?>> children = node.getChildren();

		if ((children != null) && (!children.isEmpty())) {
			for (HierarchyNode<?> child : children) {
				if(determinator.isShepherd(child)) {
					traverseshepherdTree(child,	problemReporter);
				}
			}
		} 
		
		markMySheeps(node, problemReporter);
		

	}

	private void markMySheeps(HierarchyNode<?> shepherd,
			EnclosedHierarchyProblemReporter problemReporter) {

		for (Hierarchy<SheepMarker> sheepsHierarachy : sheeps) {
			markMySheeps(shepherd, sheepsHierarachy, 
					problemReporter);
		}

	}

	private void markMySheeps(HierarchyNode<?> shepherd,
			Hierarchy<SheepMarker> sheepsHierarchy,
			EnclosedHierarchyProblemReporter problemReporter) {

		Collection<HierarchyNode<?>> mySheeps = getMySheeps(shepherd,sheepsHierarchy);

		for (HierarchyNode<?> mySheep : mySheeps) {
			HierarchyNode<SheepMarker> sheep = findSheepInHierarchy(
					sheepsHierarchy, mySheep.getClassIdentifier());

			markMySheep(shepherd, sheep, problemReporter);
		}

	}

	private void markMySheep(final HierarchyNode<?> shepherd,
			HierarchyNode<SheepMarker> sheep,
			EnclosedHierarchyProblemReporter problemReporter) {

		SheepMarker marker = sheep.getMarker();

		if (marker == null) {
			marker = new SheepMarker();
			marker.setShepherdIdentifier(shepherd.getClassIdentifier());

			sheep.setMarker(marker);

			addPredicateToAllParents(shepherd, sheep,problemReporter);
		} else {

			if(allowShepherd(sheep, shepherd,problemReporter)) {
				
				marker.setShepherdIdentifier(shepherd.getClassIdentifier());
				addPredicateToAllParents(shepherd, sheep,problemReporter);
				
			}
		}
	}

	public boolean allowShepherd(HierarchyNode<SheepMarker> sheep,
			HierarchyNode<?> shepherd, 
			EnclosedHierarchyProblemReporter problemReporter) {

		SheepMarker marker = sheep.getMarker();

		if (marker.getShepherdIdentifier() != null) {
			if(!marker.getShepherdIdentifier().equals(shepherd.getClassIdentifier())) {
				problemReporter.reportMoreThanOneShepherd(shepherd.getClassIdentifier(),
						marker.getShepherdIdentifier(), sheep.getClassIdentifier());
				return false;
			}
			
		}
		
		for (ClassIdentifier condition : marker.getShepherdPredicate()) {
				if (!condition.equals(shepherd.getClassIdentifier())) {
					if (!isBoss(shepherd,condition)) {
						problemReporter.reportBrokenShepherdHierarchy(shepherd.getClassIdentifier(),condition,sheep.getClassIdentifier());
						return false;
					}
				}
			}

		return true;
		
	}

	private boolean isBoss(HierarchyNode<?> boss, ClassIdentifier subordinate) {

		Collection<? extends HierarchyNode<?>> children = boss.getChildren();

		for (HierarchyNode<?> child : children) {
			if (child.getClassIdentifier().equals(subordinate)) {
				return true;
			} else {
				boolean ret = isBoss(child, subordinate);
				if(ret) {
					return true;
				}
			}
		}

		return false;
	}

	private void addPredicateToAllParents(HierarchyNode<?> shepherd,
			HierarchyNode<SheepMarker> sheep, EnclosedHierarchyProblemReporter problemReporter) {

		SheepMarker marker = sheep.getMarker();

		if (marker == null) {
			marker = new SheepMarker();
			sheep.setMarker(marker);
		}

		marker.addCondition(shepherd.getClassIdentifier());
		
		allowCurrentShepherd(sheep,problemReporter);
		
		if (sheep.getParent() != null) {
			addPredicateToAllParents(shepherd, sheep.getParent(),problemReporter);
		}
	}

	private void allowCurrentShepherd(HierarchyNode<SheepMarker> sheep,
			EnclosedHierarchyProblemReporter problemReporter) {
		SheepMarker marker = sheep.getMarker();
		
		if(marker.getShepherdIdentifier()!=null) {
			allowShepherd(sheep, getShepherdById(marker.getShepherdIdentifier()), problemReporter);
		}
		
	}

	private HierarchyNode<?> getShepherdById(ClassIdentifier shepherdIdentifier) {
		for(Hierarchy<?> h:shepherds) {
			HierarchyNode<?> node = h.getHierarchyNode(shepherdIdentifier);
			if(node != null) {
				return node;
			}
		}
		
		return null;
	}

	private HierarchyNode<SheepMarker> findSheepInHierarchy(
			Hierarchy<SheepMarker> sheepsHierarchy, ClassIdentifier sheepName) {

		return findSheep(sheepsHierarchy.getRoot(), sheepName);
	}

	private HierarchyNode<SheepMarker> findSheep(
			HierarchyNode<SheepMarker> node, ClassIdentifier sheepName) {

		if (node.getClassIdentifier().equals(sheepName)) {
			return node;
		}

		Collection<HierarchyNode<SheepMarker>> sheeps = node.getChildren();

		for (HierarchyNode<SheepMarker> sheep : sheeps) {
			HierarchyNode<SheepMarker> found = findSheep(sheep, sheepName);
			if (found != null) {
				return found;
			}
		}

		return null;
	}

	private Collection<HierarchyNode<?>> getMySheeps(HierarchyNode<?> shepherd, Hierarchy<SheepMarker> sheepsHierarchy) {
		Collection<? extends HierarchyNode<?>> children = shepherd
				.getChildren();

		System.err.println("shepherd: "+shepherd);
		
		Collection<HierarchyNode<?>> sheeps = new ArrayList<HierarchyNode<?>>();

		for (HierarchyNode<?> child : children) {
			if (determinator.isSheep(child)&& (null!=sheepsHierarchy.getHierarchyNode(child.getClassIdentifier()))) {
				System.err.println("has a sheep:"+child.getClassIdentifier()+" belonging to hierarchy with root: "+sheepsHierarchy.getRoot().getClassIdentifier());
				
				System.err.println(sheepsHierarchy.getHierarchyNode(child.getClassIdentifier()));
				sheeps.add(child);
			}
		}

		return sheeps;
	}

}
